package com.his.invdrug.service.Imp;

import com.his.benchwork.entity.BenchWorkConfig;
import com.his.benchwork.service.BenchWorkConfigService;
import com.his.benchwork.vo.BenchWorkConfigVo;
import com.his.dictcode.entity.DictCode;
import com.his.dictcode.service.DictCodeService;
import com.his.dictcode.vo.CacheCodeVo;
import com.his.invdrug.dao.InvDrugHeaderMapper;
import com.his.invdrug.entity.InvDrugHeader;
import com.his.invdrug.entity.InvDrugHeaderItem;
import com.his.invdrug.entity.InvDrugHeader_Detailed;
import com.his.invdrug.entity.InvDrugStorageKey;
import com.his.invdrug.service.InvDrugHeaderItemService;
import com.his.invdrug.service.InvDrugHeaderServcie;
import com.his.invdrug.service.InvDrugStorageService;
import com.his.invdrug.vo.InvDrugHeaderItemVo;
import com.his.invdrug.vo.InvDrugHeaderSearchVo;
import com.his.invdrug.vo.InvDrugHeader_DetailedVo;
import com.his.login.entity.DictEmployee;
import com.xiao.common.exception.HisExecption;
import com.xiao.common.util.BeanUtil;
import com.xiao.common.util.WorkIdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class InvDrugHeaderServiceImpl implements InvDrugHeaderServcie {
    @Autowired
    private DictCodeService dictCodeService;
    @Autowired
    private BenchWorkConfigService benchWorkConfigService;
    @Autowired
    private InvDrugHeaderItemService invDrugHeaderItemService;
    @Autowired
    private InvDrugStorageService invDrugStorageService;

    @Autowired
    private InvDrugHeaderMapper invDrugHeaderMapper;
    @Override
    public List<InvDrugHeader_Detailed> queryDrugHeaderForMainShow(DictEmployee dictEmployee, InvDrugHeaderSearchVo invDrugHeaderSearchVo) {
        invDrugHeaderSearchVo.setHospid(dictEmployee.getHOSPID());
        return invDrugHeaderMapper.selectInvDrugHeaderForMainShow(invDrugHeaderSearchVo);
    }

    @Override
    public InvDrugHeader_Detailed queryDetailed(String id) {
        return invDrugHeaderMapper.selectInvDrugHeaderDetailedByKey(id);
    }

    @Transactional
    @Override
    public void insertDrugHeader(DictEmployee dictEmployee, InvDrugHeader_DetailedVo invDrugHeaderDetailedVo) {
        //                          1.查询库存事务配置信息
        CacheCodeVo codeVo=new CacheCodeVo();
        codeVo.setCODETYPEID("000401");
        codeVo.setCODEID(invDrugHeaderDetailedVo.getInvpropid());
        codeVo.setHOSPID(dictEmployee.getHOSPID());
        List<DictCode> dictCodes =dictCodeService.getCacheCode(codeVo);
        //只能是单一的配置信息。没有查询到数据或多个数据那么配置信息存在问题
        if(dictCodes.size()!=1){
            throw HisExecption.serverErrException("事务配置信息异常，请联系管理员");
        }
        DictCode dictCode=dictCodes.get(0);
        //出入库方向 先转换成int 便于计算
        int directtion = Integer.parseInt(dictCode.getCTR1());
        //主从关系   用作是否生成从表作判断
        String ms=dictCode.getCTR2();

        //                            2.保存出入库单数据
        //生成主单ID
        String mid= WorkIdUtil.nextId();
        //获取工作台本地机构配置信息
        BenchWorkConfigVo bw=new BenchWorkConfigVo();
        bw.setBenchid(invDrugHeaderDetailedVo.getBenchWorkID());
        bw.setConfigid("0006");
        BenchWorkConfig orgPlConfig=benchWorkConfigService.select_bwconfig(bw);
        //本地机构ID
        String orgPl_ID=orgPlConfig.getConfigvalue();

        //组装出入库单主单数据
        InvDrugHeader invDrugHeader=BeanUtil.copyBean(invDrugHeaderDetailedVo, InvDrugHeader.class);
        invDrugHeader.setId(mid);
        invDrugHeader.setDirecttion((short)directtion);
        invDrugHeader.setOrgidPl(orgPl_ID);
        invDrugHeader.setState((short)0);
        invDrugHeader.setEmpidMake(dictEmployee.getEMPID());
        invDrugHeader.setEmpnameMake(dictEmployee.getEMPNAME());
        invDrugHeader.setMakeTime(new Date());
        invDrugHeader.setHospid(dictEmployee.getHOSPID());
        //生成库单号
        String inoutNum=generateInoutNum(invDrugHeader,bw);
        invDrugHeader.setInoutnum(inoutNum);

        //出入库单项目列表
        List<InvDrugHeaderItemVo> invDrugHeaderItemVos=invDrugHeaderDetailedVo.getInvDrugHeaderItemVos();
        //通过判断主从关系，决定是否生成从表
        if("1".equals(ms)){//有主从关系
            //生成从表ID
            String sid=WorkIdUtil.nextId();
            //主表和从表出入库方向相反 主出从入  主入从出，所以*-1可以得到相反的值
            int directtionSlave=directtion*-1;
            //为主表复制从表信息
            invDrugHeader.setMasterSlave((short)1);
            invDrugHeader.setMsId(sid);

            //组装从表信息
            InvDrugHeader invDrugHeaderSlave=BeanUtil.copyBean(invDrugHeaderDetailedVo, InvDrugHeader.class);
            invDrugHeaderSlave.setId(sid);
            invDrugHeaderSlave.setInoutnum(inoutNum);
            invDrugHeaderSlave.setDirecttion((short)directtionSlave);
            //库存地点为主单的来源去向机构编码
            invDrugHeaderSlave.setOrgidPl(invDrugHeader.getIoOrgidorentid());
            //来源去向为主单的库存地点
            invDrugHeaderSlave.setIoOrgidorentid(orgPl_ID);
            invDrugHeaderSlave.setState((short)0);
            invDrugHeaderSlave.setMasterSlave((short)2);//从表
            invDrugHeaderSlave.setMsId(mid);
            invDrugHeaderSlave.setEmpidMake(dictEmployee.getEMPID());
            invDrugHeaderSlave.setEmpnameMake(dictEmployee.getEMPNAME());
            invDrugHeaderSlave.setMakeTime(new Date());
            invDrugHeaderSlave.setHospid(dictEmployee.getHOSPID());

            //插入从单主表信息
            invDrugHeaderMapper.insertSelective(invDrugHeaderSlave);

            //循环组装插入从表子信息
            loopInsertHeaderItem(invDrugHeaderSlave,invDrugHeaderItemVos);
        }else{//无主从关系
            invDrugHeader.setMasterSlave((short)0);
        }
        //插入主单主表信息
        invDrugHeaderMapper.insertSelective(invDrugHeader);
        //循环组装插入主表子信息
        loopInsertHeaderItem(invDrugHeader,invDrugHeaderItemVos);
    }

    @Transactional
    @Override
    public void updateDrugHeader(DictEmployee dictEmployee, InvDrugHeader_DetailedVo invDrugHeaderDetailedVo) {
        //逻辑为修改只能修改子表数据，所以主表数据直接使用数据库的数据
        //获取主单数据
        InvDrugHeader invDrugHeader=invDrugHeaderMapper.selectByPrimaryKey(invDrugHeaderDetailedVo.getId());
        int state=invDrugHeader.getState();
        int masterSlave=invDrugHeader.getMasterSlave();
        //新建状态才能修改
        stateValid(state,0,masterSlave);
        //该实体用作为库单添加修改人信息
        InvDrugHeader invDrugHeaderForUpdate=new InvDrugHeader();
        invDrugHeaderForUpdate.setUpdaterId(dictEmployee.getEMPID());
        invDrugHeaderForUpdate.setUpdaterName(dictEmployee.getEMPNAME());
        invDrugHeaderForUpdate.setUpdateTime(new Date());

        List<InvDrugHeaderItemVo> invDrugHeaderItemVos=invDrugHeaderDetailedVo.getInvDrugHeaderItemVos();
        //思考最优方式      子项全部删除，然后再循环插入
        //删除该出入库主单所有子表
        invDrugHeaderItemService.deleteByHeaderID(invDrugHeaderDetailedVo.getId());
        //重新循环插入主单子表
        loopInsertHeaderItem(invDrugHeader,invDrugHeaderItemVos);
        if(masterSlave==1){// 有主从关系
            //获取从单数据
            InvDrugHeader invDrugHeaderSlave=invDrugHeaderMapper.selectByPrimaryKey(invDrugHeader.getMsId());
            //删除该出入库从单所有子表
            invDrugHeaderItemService.deleteByHeaderID(invDrugHeaderSlave.getId());
            //重新循环插入主单子表
            loopInsertHeaderItem(invDrugHeaderSlave,invDrugHeaderItemVos);
            //修改从单 更新者信息
            invDrugHeaderForUpdate.setId(invDrugHeaderSlave.getId());
            invDrugHeaderMapper.updateByPrimaryKeySelective(invDrugHeaderForUpdate);
        }
        //修改主单 更新着信息
        invDrugHeaderForUpdate.setId(invDrugHeader.getId());
        invDrugHeaderMapper.updateByPrimaryKeySelective(invDrugHeaderForUpdate);
    }

    @Transactional
    @Override
    public void examine(DictEmployee dictEmployee, String drugHeaderID) {
        InvDrugHeader invDrugHeader=new InvDrugHeader();
        invDrugHeader.setState((short)1);
        invDrugHeader.setEmpidCheck(dictEmployee.getEMPID());
        invDrugHeader.setEmpnameCheck(dictEmployee.getEMPNAME());
        invDrugHeader.setCheckTime(new Date());
        invDrugHeader.setUpdaterId(dictEmployee.getEMPID());
        invDrugHeader.setUpdaterName(dictEmployee.getEMPNAME());
        invDrugHeader.setUpdateTime(new Date());

        //获取该出入库单详细信息
        InvDrugHeader_Detailed invDrugHeaderDetailed=invDrugHeaderMapper.selectInvDrugHeaderDetailedByKey(drugHeaderID);
        int state=invDrugHeaderDetailed.getState();
        int masterSlave=invDrugHeaderDetailed.getMasterSlave();
        //新建状态才能审核
        stateValid(state,0,masterSlave);
        //修改主单状态
        invDrugHeader.setId(drugHeaderID);
        invDrugHeaderMapper.updateByPrimaryKeySelective(invDrugHeader);

        //根据主单项目循环处理结存
        loopControlStorage(invDrugHeaderDetailed.getInvDrugHeaderItems(),false);

        if(masterSlave==1){//有主从关系
            //根据从单项目循环处理结存
            InvDrugHeader_Detailed invDrugHeaderDetailedSlave=invDrugHeaderMapper.selectInvDrugHeaderDetailedByKey(invDrugHeaderDetailed.getMsId());

            //修改从单状态
            invDrugHeader.setId(invDrugHeaderDetailedSlave.getId());
            invDrugHeaderMapper.updateByPrimaryKeySelective(invDrugHeader);

            //根据从单项目循环处理结存
            loopControlStorage(invDrugHeaderDetailedSlave.getInvDrugHeaderItems(),false);
        }
    }

    @Override
    public void cancelExamine(DictEmployee dictEmployee, String drugHeaderID) {

        //获取该出入库单详细信息
        InvDrugHeader_Detailed invDrugHeaderDetailed=invDrugHeaderMapper.selectInvDrugHeaderDetailedByKey(drugHeaderID);
        int state=invDrugHeaderDetailed.getState();
        //主从关系
        int masterSlave=invDrugHeaderDetailed.getMasterSlave();
        //已审核状态才能取消审核
        stateValid(state,1,masterSlave);

        //拿到库单包含的项目列表，与结存数量比对。
        //      1.结存数量大于等于库单回滚数量：验证下一个项目
        //      2.如果结存数量小于库单回滚数量：该项目已经消耗过，不允许取消审核
        //    所有项目验证均通过，才实现取消操作

        //主表项目列表
        List<InvDrugHeaderItem> invDrugHeaderItems=invDrugHeaderDetailed.getInvDrugHeaderItems();
        //从单项目列表
        List<InvDrugHeaderItem> invDrugHeaderItemsSlave=new ArrayList<InvDrugHeaderItem>();
        boolean flag=true;
        if(masterSlave==1){//有主从关系，一般从表是入库主表是出库，主要问题是在从表减结存验证不通过；所以先验证从表再验证主表，节约消耗
            //获取该出入库从单详细信息
            InvDrugHeader_Detailed invDrugHeaderDetailedSlave=invDrugHeaderMapper.selectInvDrugHeaderDetailedByKey(invDrugHeaderDetailed.getMsId());
            invDrugHeaderItemsSlave=invDrugHeaderDetailedSlave.getInvDrugHeaderItems();
            flag=loopItemNumCompareStorage(invDrugHeaderItemsSlave);
            if(flag==false) throw HisExecption.serverErrException("该库单项目已经产生操作，无法取消审核！");
        }
        //   主从表验证需要全部通过后，才实现取消操作
        flag=loopItemNumCompareStorage(invDrugHeaderItems);
        if(flag==false) throw HisExecption.serverErrException("该库单项目已经产生操作，无法取消审核！");

        //修改主单影响的结存和状态
        loopControlStorage(invDrugHeaderItems,true);
        invDrugHeaderMapper.cancelExamine(drugHeaderID);
        if(masterSlave==1){//有主从关系  修改从单影响的结存和从单状态
            loopControlStorage(invDrugHeaderItemsSlave,true);
            invDrugHeaderMapper.cancelExamine(invDrugHeaderDetailed.getMsId());
        }
    }

    /**
     * 生成库单号
     * @param invDrugHeader 本地机构ID,和出入库事务
     * @param benchWorkConfigVo 工作台配置查询使用Vo
     */
    private String generateInoutNum(InvDrugHeader invDrugHeader,BenchWorkConfigVo benchWorkConfigVo){
        //查询这个机构最大的库单号
        Integer maxInoutNum=invDrugHeaderMapper.selectMaxInoutnumByOrgpl(invDrugHeader);
        if(maxInoutNum==null){//没有库单
            //去配置的初始号作为库单号
            benchWorkConfigVo.setConfigid("0022");//工作台库单编号起始号配置ID
            BenchWorkConfig bwConfig=benchWorkConfigService.select_bwconfig(benchWorkConfigVo);
            String startNumConfig=bwConfig.getConfigvalue();

            //工作台配置格式 "出入库事务&起始号"，多个配置逗号隔开
            //验证使用
            String validStr=invDrugHeader.getInvpropid()+"&";
            int startIndex=startNumConfig.indexOf(validStr);
            //从配置段中，未获取到配置信息
            if(startIndex==-1) throw HisExecption.serverErrException("工作台库单起始号配置获取失败，请联系管理员！");
            startIndex+=validStr.length();
            int endIndex=startNumConfig.indexOf(",",startIndex);
            if(endIndex==-1){
                return startNumConfig.substring(startIndex);
            }else{
                return startNumConfig.substring(startIndex,endIndex);
            }
        }else{//存在库单  取最大值+1；
            maxInoutNum++;
            return String.valueOf(maxInoutNum);
        }
    }

    /**
     * 循环插入库单子表信息
     */
    private void loopInsertHeaderItem(InvDrugHeader invDrugHeader,List<InvDrugHeaderItemVo> invDrugHeaderItemVos){
        int directtion=invDrugHeader.getDirecttion();
        for(InvDrugHeaderItemVo invDrugHeaderItemVo:invDrugHeaderItemVos){
            //出入库方向为0     子项目出入库方向以实际传入为准
            if(0==directtion){
                directtion=invDrugHeaderItemVo.getDirecttion();
            }else{
                invDrugHeaderItemVo.setDirecttion((short)directtion);
            }
            invDrugHeaderItemVo.setInoutid(invDrugHeader.getId());
            invDrugHeaderItemVo.setOrgidPl(invDrugHeader.getOrgidPl());
            invDrugHeaderItemVo.setIoOrgidorentid(invDrugHeader.getIoOrgidorentid());
            //BigDecimal 计算只能是同类型，这里需要转换成BigDecimal才能参与计算
            BigDecimal directtionBD=BigDecimal.valueOf(directtion);
            invDrugHeaderItemVo.setQuantity(
                    //单位数量*系数*出入库方向
                    invDrugHeaderItemVo.getUitquantity().multiply(invDrugHeaderItemVo.getFactor()).multiply(directtionBD)
            );
            invDrugHeaderItemService.insertSelective(invDrugHeaderItemVo);
        }
    }

    /**
     *循环操纵结存
     * @param isNegate 操作数量是否取反？ 取消审核需要把出入库数量取反操作结存来实现回溯功能
     */
    private void loopControlStorage(List<InvDrugHeaderItem> invDrugHeaderItem,boolean isNegate){
        List<InvDrugHeaderItemVo> invDrugHeaderItemVos= BeanUtil.copyList(invDrugHeaderItem,InvDrugHeaderItemVo.class);
        for(InvDrugHeaderItemVo invDrugHeaderItemVo:invDrugHeaderItemVos){
            if(isNegate){
                invDrugHeaderItemVo.setQuantity(invDrugHeaderItemVo.getQuantity().negate());
            }
            invDrugStorageService.controlStorage(invDrugHeaderItemVo);
        }
    }

    /**
     * 循环比较操作数量和结存对比，判断结存是否充足
     * @return
     */
    private boolean loopItemNumCompareStorage( List<InvDrugHeaderItem> invDrugHeaderItems){
        for(InvDrugHeaderItem invDrugHeaderItem:invDrugHeaderItems){
            InvDrugStorageKey invDrugStorageKey=new InvDrugStorageKey();
            invDrugStorageKey.setOrgidPl(invDrugHeaderItem.getOrgidPl());
            invDrugStorageKey.setHerderItemId(invDrugHeaderItem.getId());
            //这个是取消审核私用，项目操作数量为库单项目取反
            boolean flag=invDrugStorageService.temNumCompareStorage(invDrugStorageKey,invDrugHeaderItem.getQuantity().negate());
            //只要验证到一条记录库存不充足，不再继续验证直接反馈
            if(flag==false) return false;
        }
        return true;
    }

    /**
     * 库单操作验证
     * @param state 状态
     * @param stateValidnum 允许实现操作状态值
     * @param masterSlave 主从标识
     * @return
     */
    private void stateValid(int state,int stateValidnum,int masterSlave){
        if(state!=stateValidnum){
            throw HisExecption.serverErrException("库单状态已发生改变，请刷新页面");
        }
        //主从关系
        if(masterSlave==2){
            throw HisExecption.serverErrException("从单不允许执行该操作");
        }
    }
}
